home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
...taking it to the Macs!
/
...taking it to the Macs!.iso
/
Extras
/
ActiveX Mac SDK
/
ActiveX SDK
/
Container Common
/
download.cpp
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
UTF-8
Wrap
Text File
|
1997-01-02
|
26.7 KB
|
1,145 lines
|
[
TEXT/????
]
//
// DOWNLOAD.CPP
//
// Copyright (C) Microsoft Corporation, 1996
//
#include "headers.h"
#include "binhex.h"
#include "urlapi.h"
#include <Files.h>
#include <Folders.h>
#include <Resources.h>
#include <CodeFragments.h>
#include "urlmonsupport.h"
FORMATETC g_FileFormatEtc = {
CF_NULL,
NULL,
DVASPECT_CONTENT,
-1,
TYMED_FILE
};
char g_szExplorerFolderName[] = "\pExplorer";
char g_szCacheFolderName[] = "\pActiveX Cache";
OLECHAR g_szFileVersion[] = "FileVersion";
OLECHAR g_szDestDir[] = "destdir";
OLECHAR g_szVersion[] = "version";
BOOL FindPrivateFolder(short *foundvRefNum, long *foundDirID, long ParentDirID, StringPtr FolderName);
BOOL FindExplorerFolder(short *foundvRefNum, long *foundDirID);
BOOL GetVersionFromString(LPOLESTR pszString, LPDWORD pdwVersion);
BOOL CheckFileVersion(LPOLESTR pszFile, DWORD dwMinimumVersion, BOOL fUseExtensionsFolder);
HRESULT RegisterOleFragment(FSSpec *spec, BOOL fRegister);
typedef HRESULT ( *LPFNREGISTERSERVER)(void);
CCodeDownload::CCodeDownload()
{
mRefCount = 1; // reference count of object
mEnumKeyP = 0;
mSiteP = NULL;
mQueueP = NULL;
mDownloadResult = 0;
mStreamDataP = NULL;
mProcessINF = false;
mEnd = NULL;
mCurrent = NULL;
mLastSectionMatch = NULL;
mStreamLength = 0;
}
CCodeDownload::~CCodeDownload()
{
}
//
// CCodeDownload::QueueDownload
//
void
CCodeDownload::QueueDownload(CXSite *inSiteP)
{
QUEUED_DOWNLOAD *QueueEntryP;
if (mSiteP == NULL) {
// No downloads are in progress, likely because this is the first one
// in, so just start it up.
StartDownload(inSiteP);
} else {
if ((QueueEntryP = (QUEUED_DOWNLOAD *)
CoTaskMemAlloc(sizeof(QUEUED_DOWNLOAD))) != NULL) {
QueueEntryP->SiteP = inSiteP;
QueueEntryP->NextP = mQueueP;
mQueueP = QueueEntryP;
}
}
}
//
// CCodeDownload::StartNextDownload
//
void
CCodeDownload::StartNextDownload(void)
{
QUEUED_DOWNLOAD *QueueEntryP;
CXSite *SiteP;
HRESULT hr;
while (mQueueP != NULL)
{
// Unlink the queued entry from the list.
QueueEntryP = mQueueP;
mQueueP = QueueEntryP->NextP;
SiteP = QueueEntryP->SiteP;
CoTaskMemFree(QueueEntryP);
// Check if the object still requires a download. The download may no
// longer be necessary if another site already downloaded all the code
// that this site requires.
hr = SiteP->CreateControl(false);
if (hr == REGDB_E_CLASSNOTREG || hr == CO_E_DLLNOTFOUND)
{
if (StartDownload(SiteP))
return;
}
SiteP->Release();
}
}
//
// CCodeDownload::StartDownload
//
// Fires off a new code download cycle.
//
// Note as a result of being called from StartNextDownload, we may not be
// running in the "context" of the Netscape plugin that this pSite relates to.
//
BOOL
CCodeDownload::StartDownload(CXSite *inSiteP)
{
BOOL fSuccess = false;
FILEXTN FileType = GetFileTypeFromExtension(inSiteP->m_pszCodeURL);
mProcessINF = FileType == FILEXTN_INF;
mSiteP = inSiteP; // This is the current download site
if(FileType != FILEXTN_UNKNOWN)
fSuccess = OpenStream(inSiteP, mProcessINF, inSiteP->m_pszCodeURL);
// The site no longer needs this memory, so free it up now.
// CoTaskMemFree(inSiteP->m_pszCodeURL);
DisposePtr(inSiteP->m_pszCodeURL);
inSiteP->m_pszCodeURL = NULL;
if(!fSuccess)
{
if(mSiteP)
mSiteP->Release();
mSiteP = NULL;
}
return fSuccess;
}
BOOL
CCodeDownload::OpenStream(CXSite *inSiteP, Boolean IsINFFile, LPOLESTR URLString)
{
BOOL fSuccess = FALSE;
{
LPBINDHOST BindSiteP;
LPENUMFORMATETC pEnum = NULL;
LPMONIKER URLMoniker = NULL;
HRESULT hr = E_FAIL;
LPBINDCTX BindContext = NULL;
inSiteP->QueryInterface(IID_IBindHost, (LPVOID *) &BindSiteP);
if(BindSiteP)
{
BindSiteP->CreateMoniker((LPOLESTR)URLString, NULL, &URLMoniker, 0);
if(URLMoniker)
{
LPVOID Dummy;
// if inf file, don't register enumerator so we get a stream not a file
if(!IsINFFile)
{
CreateFormatEnumerator(1, &g_FileFormatEtc, &pEnum);
if(pEnum)
{
CreateBindCtx( 0, &BindContext);
if(BindContext)
RegisterFormatEnumerator(BindContext, pEnum, 0);
}
}
if(IsINFFile || BindContext)
{
hr = BindSiteP->MonikerBindToStorage(URLMoniker, BindContext,
(IBindStatusCallback*)this, IID_IStream, &Dummy);
if(hr == E_PENDING)
hr = S_OK;
if(BindContext)
BindContext->Release();
URLMoniker->Release();
}
if(pEnum)
pEnum->Release();
}
BindSiteP->Release();
}
if(hr == S_OK)
{
fSuccess = TRUE;
}
}
return fSuccess;
}
//
// CCodeDownload::ProcessNextSection
//
void
CCodeDownload::ProcessNextSection(void)
{
BOOL fResult;
OLECHAR szKeyName[MAX_PATH];
OLECHAR szBuffer[64];
OLECHAR szURL[MAX_URL_STRING];
BOOL fUseExtensionsFolder;
CLSID clsid;
DWORD dwMinimumVersion;
// Get the name of the next section from the .inf to download.
ProcessNext:
if (mEnumKeyP == NULL) {
fResult = GetFirstKey("Add.Code", &mEnumKeyP, szKeyName,
sizeof(szKeyName));
} else {
fResult = GetNextKey(&mEnumKeyP, szKeyName,
sizeof(szKeyName));
}
if (!fResult)
{
// destroy the inf file data
CoTaskMemFree(mStreamDataP);
mStreamDataP = NULL;
// There aren't any more sections to process, so attempt to create the
// site's object.
mSiteP->CreateControl(false);
mSiteP->Release();
mSiteP = NULL;
mEnumKeyP = NULL;
mLastSectionMatch = NULL;
goto StartNextDownload;
}
GetProfileString("Add.Code", szKeyName, szKeyName,
sizeof(szKeyName));
// Get the minimum required version of the module.
dwMinimumVersion = 0;
if (GetProfileString(szKeyName, g_szFileVersion, szBuffer,
sizeof(szBuffer))) {
if (szBuffer[0] != '\0') {
// If incorrectly formed, the following will return FALSE and
// dwMinimumVersion will continue to be zero. Should we treat that
// as a download failure?
GetVersionFromString(szBuffer, &dwMinimumVersion);
}
}
// Get the folder that this file is supposed to go in-- either the ActiveX
// Cache or the Extensions folder.
fUseExtensionsFolder = FALSE;
if (GetProfileString(szKeyName, g_szDestDir, szBuffer,
sizeof(szBuffer))) {
// These magic numbers are LDID_WIN and LDID_SYS as defined by Windows
// .INF files.
if (strcmp(g_szDestDir, "10") || strcmp(g_szDestDir, "11"))
fUseExtensionsFolder = TRUE;
}
// Get the URL that points at the code for this component.
if (!GetProfileString(szKeyName, "file-mac-ppc", szURL,
sizeof(szURL))) {
if (!GetProfileString(szKeyName, "file", szURL,
sizeof(szURL))) {
szURL[0] = '\0';
}
}
// If the file's URL is the magic "ignore", then this file is not required
// for this platform, so we can skip to the next .inf line.
if (strcmp(szURL, "ignore") == 0)
goto ProcessNext;
// Determine if a locally installed copy of the code exists and if it's an
// adequate version to use.
if (GetProfileString(szKeyName, "clsid", szBuffer,
sizeof(szBuffer))) {
// If the string is incorrectly formed, bail out.
if (CLSIDFromString(szBuffer, &clsid) != S_OK)
goto StartNextDownload;
// If the handler for this CLSID is okay, then we're fine... go to the
// next line.
if (CheckCLSIDVersion(clsid, dwMinimumVersion))
goto ProcessNext;
} else {
// If this file version is okay, then we're fine... go to the next line.
if (CheckFileVersion(szKeyName, dwMinimumVersion, fUseExtensionsFolder))
goto ProcessNext;
}
// The file extension must be BinHex... we do not support recursive .INF
// files and we don't know how to handle any other file type.
if (GetFileTypeFromExtension(szURL) != FILEXTN_HQX)
goto StartNextDownload;
if(OpenStream(mSiteP, false, szURL))
{
return;
}
StartNextDownload:
StartNextDownload();
return;
}
//
// CCodeDownload::InfAvailable
//
// Called by the .inf stream notification object when its transfer is complete.
//
void
CCodeDownload::InfAvailable(BOOL fSuccess)
{
if (fSuccess)
{
ProcessNextSection();
}
else
{
// If the transfer was not successful, bail on this download and start
// the next one.
if(mSiteP)
mSiteP->Release();
mSiteP = NULL;
StartNextDownload();
}
}
//
// CCodeDownload::CodeAvailable
//
// Called by the code stream notification object when its transfer is complete.
//
void
CCodeDownload::CodeAvailable(BOOL fSuccess)
{
if (fSuccess)
{
if (mProcessINF)
{
ProcessNextSection();
}
else
{
// This was a straight code download, not driven by an .INF, so we
// can attempt to create the object now.
mSiteP->CreateControl(false);
mSiteP->Release();
mSiteP = NULL;
StartNextDownload();
}
}
else
{
// If the transfer was not successful, bail on this download and start
// the next one.
if(mSiteP)
mSiteP->Release();
mSiteP = NULL;
StartNextDownload();
}
}
//
// FindPrivateFolder
//
// Returns the volume and directory reference of the specified folder name from
// the specified parent directory, making the directory if necessary.
//
BOOL FindPrivateFolder(short *foundvRefNum, long *foundDirID, long ParentDirID, StringPtr FolderName)
{
CInfoPBRec pb;
HFileParam hb;
pb.dirInfo.ioNamePtr = FolderName;
pb.dirInfo.ioFDirIndex = 0;
pb.dirInfo.ioVRefNum = *foundvRefNum;
pb.dirInfo.ioDrDirID = ParentDirID;
pb.dirInfo.ioACUser = 0;
if (PBGetCatInfoSync(&pb) == noErr) {
// The Win32 code currently bails its cache folder is not a folder.
if (pb.dirInfo.ioFlAttrib & ioDirMask) {
*foundDirID = pb.dirInfo.ioDrDirID;
return TRUE;
}
} else {
hb.ioNamePtr = (StringPtr) FolderName;
hb.ioVRefNum = *foundvRefNum;
hb.ioDirID = ParentDirID;
if (PBDirCreateSync((union HParamBlockRec *) &hb) == noErr) {
*foundDirID = hb.ioDirID;
return TRUE;
}
}
return FALSE;
}
//
// FindExplorerFolder
//
// Returns the volume and directory reference of the Internet Explorer folder.
//
BOOL FindExplorerFolder(short *foundvRefNum, long *foundDirID)
{
BOOL fResult;
long ParentDirID;
if (FindFolder((short) kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
foundvRefNum, &ParentDirID) == noErr) {
fResult = FindPrivateFolder(foundvRefNum, foundDirID, ParentDirID,
(StringPtr) g_szExplorerFolderName);
} else {
fResult = FALSE;
}
return fResult;
}
//
// FindActiveXCacheFolder
//
// Returns the volume and directory reference of the ActiveX cache folder.
//
BOOL
FindActiveXCacheFolder(short *foundvRefNum, long *foundDirID)
{
BOOL fResult;
long ParentDirID;
if (FindExplorerFolder(foundvRefNum, &ParentDirID)) {
fResult = FindPrivateFolder(foundvRefNum, foundDirID, ParentDirID,
(StringPtr) g_szCacheFolderName);
} else {
fResult = FALSE;
}
return fResult;
}
//
// FindDownloadFolder
//
// Returns the volume and directory reference of the ActiveX cache or the
// Extensions folder
//
BOOL
FindDownloadFolder(short *foundvRefNum, long *foundDirID, BOOL fExtensionsFolder)
{
BOOL fResult;
if (fExtensionsFolder) {
fResult = (FindFolder((short) kOnSystemDisk, kExtensionFolderType,
kCreateFolder, foundvRefNum, foundDirID) == noErr);
} else {
fResult = FindActiveXCacheFolder(foundvRefNum, foundDirID);
}
return fResult;
}
//
// GetFileTypeFromExtension
//
FILEXTN
GetFileTypeFromExtension(LPOLESTR pszURL)
{
FILEXTN filextn;
LPOLESTR pszCurrent;
LPOLESTR pszExtension;
for (pszCurrent = pszURL, pszExtension = NULL; *pszCurrent; pszCurrent++) {
if (*pszCurrent == '.') {
pszExtension = pszCurrent + 1;
} else {
if (*pszCurrent == '/' || *pszCurrent == '\\') {
pszExtension = NULL;
}
}
}
filextn = FILEXTN_UNKNOWN;
if (strcmp(pszExtension, "hqx") == 0) {
filextn = FILEXTN_HQX;
} else {
if (strcmp(pszExtension, "inf") == 0) {
filextn = FILEXTN_INF;
}
}
return filextn;
}
//
// GetVersionFromFile
//
// Returns the version from the specified file's "vers" resource.
//
BOOL
GetVersionFromFile(FSSpec *spec, LPDWORD pdwVersion)
{
BOOL fResult = FALSE;
short resFile;
Handle hResource;
if ((resFile = FSpOpenResFile(spec, fsRdPerm)) != -1) {
if ((hResource = Get1Resource('vers', 1)) != nil) {
if (GetHandleSize(hResource) > sizeof(DWORD)) {
// Extract the version number from the resource, stripping off
// the release stage byte.
*pdwVersion = (*((LPDWORD)(*hResource))) & 0xFFFF00FF;
fResult = TRUE;
}
}
CloseResFile(resFile);
}
return fResult;
}
//
// GetVersionFromString
//
BOOL
GetVersionFromString(LPOLESTR pszString, LPDWORD pdwVersion)
{
BYTE parts[4] = { 0, 0, 0, 0 };
UINT CurrentPart = 0;
UINT n = 0;
while (TRUE) {
if (*pszString == ',' || *pszString == '\0') {
if (CurrentPart >= 4)
return FALSE;
parts[CurrentPart++] = (BYTE) n;
n = 0;
if (*pszString == '\0')
break;
} else {
if (*pszString < '0' || *pszString > '9')
return FALSE;
// The 'vers' resource stores numbers in BCD format.
n = n * 16 + (*pszString - '0');
}
pszString++;
}
// Note that we ignore the "c" element from "version=a,b,c,d" because
// GetVersionFromFile returns a number of the form "a,b,0,d"
*pdwVersion = (((DWORD) parts[0]) << 24) + (((DWORD) parts[1]) << 16) +
(((DWORD) parts[3]) << 0);
return TRUE;
}
//
// GetVersionFromURL
//
// Returns the "version" from the specified URL string. As a side effect, the
// string will have any optional parameters "stripped off" by nulling out any
// present '#' character.
//
BOOL
GetVersionFromURL(LPOLESTR pszURL, LPDWORD pdwVersion)
{
LPOLESTR pszString;
LPOLESTR pszVersionStart;
if ((pszString = strchr(pszURL, '#')) == NULL)
return FALSE;
*pszString++ = '\0'; // Zap the '#' character
pszVersionStart = pszString;
if ((pszString = strchr(pszString, '=')) == NULL)
return FALSE;
*pszString++ = '\0'; // Zap the '=' character
if (strcmp(pszVersionStart, g_szVersion) != 0)
return FALSE;
return GetVersionFromString(pszString, pdwVersion);
}
//
// CheckFileVersion
//
// Returns TRUE if the specified file from the specified folder is at least the
// specified minimum version, else returns FALSE.
//
BOOL
CheckFileVersion(LPOLESTR pszFile, DWORD dwMinimumVersion, BOOL
fUseExtensionsFolder)
{
BOOL fResult = FALSE;
DWORD dwFileVersion;
FSSpec spec;
if (FindDownloadFolder(&spec.vRefNum, &spec.parID, fUseExtensionsFolder)) {
c2pstr(pszFile);
if (FSMakeFSSpec(spec.vRefNum, spec.parID, (StringPtr) pszFile,
&spec) == noErr) {
if (dwMinimumVersion != 0) {
if (GetVersionFromFile(&spec, &dwFileVersion) &&
(dwMinimumVersion <= dwFileVersion))
fResult = TRUE;
} else {
// No minimum version specified; simply checks that the file
// exists.
fResult = TRUE;
}
}
p2cstr((StringPtr) pszFile);
}
return fResult;
}
//
// CCodeDownload::IUnknown::QueryInterface
//
// Returns a pointer to the specified interface on a component to which a
// client currently holds an interface pointer.
//
STDMETHODIMP
CCodeDownload::QueryInterface(REFIID RefID, void** Obj)
{
void* pv = nil;
if (RefID == IID_IUnknown || RefID == IID_IBindStatusCallback)
pv = (LPVOID)(IBindStatusCallback* ) this;
*Obj = pv;
// if we got an interface, ref it and return ok
if ( pv )
{
((IUnknown*) pv)->AddRef();
return S_OK;
}
else
return E_NOINTERFACE;
}
//
// CCodeDownload::IUnknown::AddRef
//
// Increments the reference count for the calling interface.
//
STDMETHODIMP_(ULONG)
CCodeDownload::AddRef(void)
{
return ++mRefCount;
}
//
// CCodeDownload::IUnknown::Release
//
// Decrements the reference count for the calling interface on a object. If
// the reference count on the object falls to zero, the object is freed.
//
STDMETHODIMP_(ULONG)
CCodeDownload::Release(void)
{
if (--mRefCount != 0)
return mRefCount;
// delete this;
return 0;
}
//=--------------------------------------------------------------------------=
// CCodeDownload::IBindStatusCallback::OnStartBinding
//=--------------------------------------------------------------------------=
//
STDMETHODIMP
CCodeDownload::OnStartBinding(DWORD BSCOption, IBinding* aBinding)
{
#pragma unused (BSCOption, aBinding)
// m_pib->AddRef();
mDownloadResult = FALSE;
return S_OK;
}
//=--------------------------------------------------------------------------=
// CCodeDownload::IBindStatusCallback::GetPriority
//=--------------------------------------------------------------------------=
//
STDMETHODIMP
CCodeDownload::GetPriority(LONG *Priority)
{
*Priority = 0;
return S_OK;
}
//=--------------------------------------------------------------------------=
// CCodeDownload::IBindStatusCallback::OnLowResource
//=--------------------------------------------------------------------------=
//
STDMETHODIMP
CCodeDownload::OnLowResource(DWORD reserved)
{
#pragma unused (reserved)
return S_OK;
}
//=--------------------------------------------------------------------------=
// CCodeDownload::IBindStatusCallback::OnProgress
//=--------------------------------------------------------------------------=
//
STDMETHODIMP
CCodeDownload::OnProgress(ULONG Progress, ULONG ProgressMax, ULONG StatusCode, const char* StatusText)
{
#pragma unused (Progress, ProgressMax, StatusCode, StatusText)
return S_OK;
}
//=--------------------------------------------------------------------------=
// CCodeDownload::IBindStatusCallback::OnStopBinding
//=--------------------------------------------------------------------------=
//
STDMETHODIMP
CCodeDownload::OnStopBinding(HRESULT hresult, const char* Error)
{
#pragma unused (hresult, Error)
// if (m_pib != NULL)
// m_pib->Release();
// m_pib = NULL;
CodeAvailable(mDownloadResult);
return S_OK;
}
//=--------------------------------------------------------------------------=
// CCodeDownload::IBindStatusCallback::GetBindInfo
//=--------------------------------------------------------------------------=
//
STDMETHODIMP
CCodeDownload::GetBindInfo(DWORD* BINDF, BINDINFO *BindInfo)
{
*BINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE;
BindInfo->dwBindVerb = BINDVERB_GET;
return S_OK;
}
//=--------------------------------------------------------------------------=
// CCodeDownload::IBindStatusCallback::OnDataAvailable
//=--------------------------------------------------------------------------=
//
STDMETHODIMP
CCodeDownload::OnDataAvailable(DWORD BSCF, DWORD Size, FORMATETC* FormatEtc, STGMEDIUM* StgMedium)
{
#pragma unused (FormatEtc)
if (StgMedium->tymed == TYMED_FILE)
{
Str255 fileName;
FSSpec BinHexFile;
FSSpec UnpackedFile;
if (StgMedium->lpszFileName != NULL) {
strncpy((char *) fileName, StgMedium->lpszFileName, 256);
c2pstr((char *) fileName);
if (FSMakeFSSpec(0, 0, fileName, &BinHexFile) == noErr)
{
if (IsBinHexFile(&BinHexFile, &UnpackedFile))
{
if (FindDownloadFolder(&UnpackedFile.vRefNum,
&UnpackedFile.parID, false))
{
if (UnpackBinHexFile(&BinHexFile, &UnpackedFile, NULL))
{
RegisterOleFragment(&UnpackedFile, TRUE);
mDownloadResult = TRUE;
}
}
}
}
}
CoTaskMemFree(StgMedium->lpszFileName);
}
else if ((StgMedium->tymed == TYMED_ISTREAM) && ((BSCF & BSCF_LASTDATANOTIFICATION) == 0))
{
if(!mStreamDataP)
{
mStreamDataP = (unsigned char *)CoTaskMemAlloc(Size);
mStreamLength = 0;
}
else
mStreamDataP = (unsigned char *) CoTaskMemRealloc(mStreamDataP, mStreamLength + Size);
StgMedium->pstm->Read(mStreamDataP + mStreamLength, Size, NULL);
mStreamLength += Size;
mDownloadResult = TRUE;
mEnd = (LPOLESTR) mStreamDataP + mStreamLength;
}
if (StgMedium->pUnkForRelease != NULL)
StgMedium->pUnkForRelease->Release();
return S_OK;
}
//=--------------------------------------------------------------------------=
// CCodeDownload::IBindStatusCallback::OnObjectAvailable
//=--------------------------------------------------------------------------=
//
STDMETHODIMP
CCodeDownload::OnObjectAvailable(REFIID RefID, IUnknown* Unknown)
{
#pragma unused (RefID, Unknown)
return S_OK;
}
//
// RegisterOleFragment
//
// Calls the DllRegisterServer or DllUnregisterServer entrypoint of the
// specified file.
//
HRESULT
RegisterOleFragment(FSSpec *spec, BOOL fRegister)
{
HRESULT hr;
CFragConnectionID connID;
Ptr mainAddr;
Str255 errName;
StringPtr psymName;
Ptr symAddr;
CFragSymbolClass symClass;
if (GetDiskFragment(spec, 0, kCFragGoesToEOF, nil, kLoadCFrag, &connID, &mainAddr,
errName) == noErr) {
psymName = fRegister ? "\pDllRegisterServer" : "\pDllUnregisterServer";
if (FindSymbol(connID, psymName, &symAddr, &symClass) == noErr) {
// DllRegisterServer/DllUnregisterServer have the same prototype.
hr = ((LPFNREGISTERSERVER) symAddr)();
} else {
// We won't consider it an error if this fragment isn't self-
// registering.
hr = S_OK;
}
CloseConnection(&connID);
} else {
hr = ResultFromScode(CO_E_DLLNOTFOUND);
}
return hr;
}
#define IsSpace(ch) ((ch)==' ' || (ch)=='\t' || (ch)=='\n' || (ch)=='\r')
#define IsEndOfLine(ch) ((ch)=='\n' || (ch)=='\r')
#define ToUpper(ch) (((ch)>='A'&&(ch)<='Z') ? ((ch)-'A'+'a') : (ch))
#define min(a,b) (((a) < (b)) ? (a) : (b))
//
// CInfStreamNotify::SkipWhitespace
//
// Moves the current pointer ahead to the first non-whitespace character.
//
void
CCodeDownload::SkipWhitespace(void)
{
while (IsSpace(*mCurrent) && mCurrent < mEnd)
mCurrent++;
}
//
// CCodeDownload::SkipToNextLine
//
// Moves the current pointer ahead to the first non-whitespace character after
// a end-of-line character.
//
void
CCodeDownload::SkipToNextLine(void)
{
char ch;
do {
while (mCurrent < mEnd) {
ch = *mCurrent++;
if (IsEndOfLine(ch))
break;
}
SkipWhitespace();
} while (mCurrent < mEnd && *mCurrent == ';');
}
//
// CCodeDownload::FindSection
//
// Moves the current pointer ahead to the first line after the section that
// matches the specified section name. Returns TRUE if the section was found,
// else FALSE.
//
BOOL
CCodeDownload::FindSection(LPOLESTR pszSection)
{
BOOL fSecondPass;
LPOLESTR pszStop;
LPOLESTR pszSectionMatch;
LPOLESTR pszSectionStart;
fSecondPass = FALSE;
// Start scanning from the start of the memory buffer or from the last
// section where we found a match as an optimization.
mCurrent = (mLastSectionMatch != NULL) ? mLastSectionMatch :
(LPOLESTR) mStreamDataP;
pszStop = mEnd;
DoNextPass:
SkipWhitespace();
while (mCurrent < pszStop) {
// As an optimization, if this is the section we're looking for,
// we'll keep a pointer to it so that the next time FindSection is
// called, we can start from there and catch multiple requests for
// the same section.
pszSectionStart = mCurrent;
if (*mCurrent++ == '[') {
pszSectionMatch = pszSection;
while (mCurrent < pszStop && ToUpper(*mCurrent) ==
ToUpper(*pszSectionMatch)) {
mCurrent++;
pszSectionMatch++;
if (*pszSectionMatch == '\0') {
if (mCurrent < pszStop && *mCurrent++ == ']') {
mLastSectionMatch = pszSectionStart;
SkipToNextLine();
return TRUE;
}
break;
}
}
}
SkipToNextLine();
}
// If this is our first pass and we didn't begin from the base of the
// memory buffer, then do another pass, this time from the base of the
// buffer.
if (!fSecondPass && mLastSectionMatch != NULL) {
mCurrent = (LPOLESTR) mStreamDataP;
pszStop = mLastSectionMatch;
fSecondPass = TRUE;
goto DoNextPass;
}
return FALSE;
}
//
// CCodeDownload::GetProfileString
//
// Reads a string from the .inf file much like the Win32 GetProfileString API.
// Returns TRUE if the specified section/key were found, else FALSE.
//
BOOL
CCodeDownload::GetProfileString(LPOLESTR pszSection, LPOLESTR pszKey,
LPOLESTR pszBuffer, UINT BufferSize)
{
LPOLESTR pszKeyMatch;
LPOLESTR pszBufferBase;
// FindSection will move the current pointer to the line immediately after
// the section name, if the section exists.
if (!FindSection(pszSection))
return FALSE;
while (mCurrent < mEnd && *mCurrent != '[') {
pszKeyMatch = pszKey;
while (mCurrent < mEnd && ToUpper(*mCurrent) ==
ToUpper(*pszKeyMatch)) {
mCurrent++;
pszKeyMatch++;
if (*pszKeyMatch == '\0') {
SkipWhitespace();
if (mCurrent < mEnd && *mCurrent == '=') {
mCurrent++;
SkipWhitespace();
pszBufferBase = pszBuffer;
while (mCurrent < mEnd &&
!IsEndOfLine(*mCurrent) && *mCurrent != ';' &&
BufferSize-- > 0) {
*pszBuffer++ = *mCurrent++;
}
// Strip off any trailing whitespace from the buffer.
while (pszBuffer > pszBufferBase && IsSpace(*(pszBuffer-1)))
pszBuffer--;
*pszBuffer = '\0';
return TRUE;
}
break;
}
}
SkipToNextLine();
}
return FALSE;
}
//
// CCodeDownload::GetKeyCommon
//
// Common worker routine for GetFirstKeyName and GetNextKeyName. On entry, the
// current pointer should be positioned at the first non-whitespace character
// of the next candidate line.
//
BOOL
CCodeDownload::GetKeyCommon(LPVOID *pvEnumKey, LPOLESTR pszBuffer, UINT
BufferSize)
{
LPOLESTR pszKeyStart;
UINT BytesToCopy;
while (mCurrent < mEnd && *mCurrent != '[') {
pszKeyStart = mCurrent;
while (mCurrent < mEnd && !IsEndOfLine(*mCurrent)) {
if (*mCurrent == '=') {
while (mCurrent > pszKeyStart && IsSpace(*(mCurrent-1)))
mCurrent--;
BytesToCopy = min((UINT) (mCurrent - pszKeyStart),
BufferSize - 1);
memcpy(pszBuffer, pszKeyStart, BytesToCopy);
pszBuffer[BytesToCopy] = '\0';
*pvEnumKey = (LPVOID) pszKeyStart;
return TRUE;
}
mCurrent++;
}
SkipToNextLine();
}
return FALSE;
}
//
// CCodeDownload::GetFirstKey
//
// Returns the first key from the specified section. On return, pvEnumKey is a
// "handle" that can be used in subsequents calls to GetNextKey.
//
BOOL
CCodeDownload::GetFirstKey(LPOLESTR pszSection, LPVOID *pvEnumKey, LPOLESTR
pszBuffer, UINT BufferSize)
{
// FindSection will move the current pointer to the line immediately after
// the section name, if the section exists.
if (!FindSection(pszSection))
return FALSE;
return GetKeyCommon(pvEnumKey, pszBuffer, BufferSize);
}
//
// CCodeDownload::GetNextKey
//
// Returns the next key from the specified section using the "handle" returned
// from previous GetFirstKey or GetNextKey calls.
//
BOOL
CCodeDownload::GetNextKey(LPVOID *pvEnumKey, LPOLESTR pszBuffer, UINT
BufferSize)
{
// On entry, pvEnumKey points at the line that contained the last
// successful enumeration.
mCurrent = (LPOLESTR) *pvEnumKey;
SkipToNextLine();
return GetKeyCommon(pvEnumKey, pszBuffer, BufferSize);
}
//
// CheckCLSIDVersion
//
// Returns TRUE if the inproc server for the specified CLSID is at least the
// specified minimum version, else returns FALSE.
//
BOOL
CheckCLSIDVersion(REFCLSID /* rclsid */, DWORD /* dwMinimumVersion */)
{
BOOL fResult = FALSE;
#if 0
// BUGBUG
FSSpec spec;
DWORD dwFileVersion;
if (SUCCEEDED(CoGetClassFSSpec(rclsid, CLSCTX_INPROC_SERVER, &spec))
{
if (dwMinimumVersion != 0)
{
if (GetVersionFromFile(&spec, &dwFileVersion) &&
(dwMinimumVersion <= dwFileVersion))
fResult = TRUE;
}
else
{
// No minimum version specified; simply checks that a handler for
// the CLSID exists.
fResult = TRUE;
}
}
#endif
return fResult;
}